// Effective C++ 条款41：了解隐式接口和编译期多态

// 核心概念
// 条款41讨论了C++中两种不同的接口和多态形式：
// 1.显式接口与运行期多态：基于虚函数的传统面向对象编程
// 2.隐式接口与编译期多态：基于模板和泛型编程

// 显式接口与运行期多态
// 传统的面向对象编程使用显式接口和运行期多态：
class Widget
{
public:
    virtual void draw() const;
    virtual void resize(int width, int height);
    // 其他虚函数...
};

void manipulateWidget(Widget &w)
{
    w.draw(); // 通过虚函数调用实现多态
    w.resize(50, 50);
}
// 特点：
// 显式接口：通过类声明明确定义（函数签名）
// 运行期多态：通过虚函数表在运行时解析函数调用
// 基于继承：需要继承关系建立类型之间的联系

// 隐式接口与编译期多态
// 模板和泛型编程使用隐式接口和编译期多态：
template <typename T>
void processObject(T &obj)
{
    if (obj.size() > 10 && obj != someNastyWidget)
    {
        T temp(obj);
        temp.normalize();
        temp.swap(obj);
    }
}
// 特点：
// 隐式接口：由有效表达式决定，而非函数签名
// 编译期多态：通过模板实例化和函数重载解析在编译期实现
// 基于约束：只要满足表达式约束，任何类型都可使用

// 隐式接口详解
// 隐式接口由模板参数必须支持的表达式集合定义：
template <typename T>
void doSomething(T &obj)
{
    // 隐式接口要求：
    obj.begin();    // T必须有begin()方法
    obj.end();      // T必须有end()方法
    obj[0];         // T必须支持operator[]
    obj.size() > 0; // T必须有size()方法且返回值可与0比较
    T copy = obj;   // T必须可复制构造
    copy.swap(obj); // T必须有swap方法
}
// 关键点：
// 1.接口由有效表达式定义，而非函数签名
// 2.约束是语义的，而非语法的
// 3.接口是隐式的，没有显式声明

// 编译期多态详解
// 编译期多态通过模板实例化实现不同类型的不同行为：
template <typename T>
void print(const T &obj)
{
    std::cout << obj; // 对不同T类型调用不同的operator<<
}

print(42);                         // 调用operator<<(int)
print("hello");                    // 调用operator<<(const char*)
print(std::complex<double>(1, 2)); // 调用operator<<(complex)

// 关键点：
// 1.不同类型的不同行为在编译期确定
// 2.没有运行时开销
// 3.通过模板实例化和函数重载解析实现

// 隐式接口与编译期多态的优势
// 1.性能：没有虚函数调用开销
// 2.灵活性：无需继承关系，只需满足接口约束
// 3.可扩展性：可以为已有类型添加新功能
// 4.静态类型安全：类型错误在编译期检测

// 实际应用示例
// 1. STL算法与迭代器
// STL是隐式接口和编译期多态的典范：
template <typename Iter, typename T>
Iter find(Iter first, Iter last, const T &value)
{
    while (first != last && *first != value)
    {
        ++first;
    }
    return first;
}

// 可用于任何满足迭代器隐式接口的类型
std::vector<int> v = {1, 2, 3, 4};
auto it = find(v.begin(), v.end(), 3);

std::list<std::string> l = {"a", "b", "c"};
auto it2 = find(l.begin(), l.end(), "b");

int arr[] = {5, 6, 7, 8};
auto it3 = find(arr, arr + 4, 7);

// 2. SFINAE和类型特性
// 通过SFINAE（Substitution Failure Is Not An Error）实现编译期多态：
// 有print方法的类型版本
template <typename T>
auto print_value(const T &x) -> decltype(x.print(), void())
{
    x.print();
}

// 可以用<<输出的类型版本
template <typename T>
auto print_value(const T &x) -> decltype(std::cout << x, void())
{
    std::cout << x;
}

// 其他类型的默认版本
template <typename T>
void print_value(const T &)
{
    std::cout << "No suitable print method found";
}

// 3. 策略模式的编译期实现
// 使用模板参数作为策略：
template <typename SortPolicy>
void sortData(std::vector<int> &data)
{
    SortPolicy::sort(data.begin(), data.end());
}

struct QuickSortPolicy
{
    template <typename Iter>
    static void sort(Iter begin, Iter end)
    {
        // 快速排序实现
    }
};

struct MergeSortPolicy
{
    template <typename Iter>
    static void sort(Iter begin, Iter end)
    {
        // 归并排序实现
    }
};

std::vector<int> v = {5, 3, 8, 1, 2};
sortData<QuickSortPolicy>(v); // 使用快速排序
sortData<MergeSortPolicy>(v); // 使用归并排序

// 隐式接口与编译期多态的限制
// 1.错误消息复杂：模板错误消息通常难以理解
// 2.编译时间增加：大量模板实例化可能导致编译时间长
// 3.代码膨胀：每个不同类型都生成独立代码
// 4.约束表达困难：C++20之前难以明确表达类型约束

// 总结
// 条款41告诉我们：
// 类和模板都支持接口和多态
// 对类而言，接口是显式的，以函数签名为中心；多态则是通过虚函数在运行期发生
// 对模板参数而言，接口是隐式的，以有效表达式为中心；多态则是通过模板实例化和函数重载解析在编译期发生
// 理解这两种不同的接口和多态形式，可以帮助我们更好地设计C++代码，选择最适合问题的解决方案。